package org.kernelab.numeric;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.swing.JFrame;

import org.kernelab.basis.Function;
import org.kernelab.basis.Interval;
import org.kernelab.basis.Tools;
import org.kernelab.basis.gui.chart.GraphicsPainterAdapter;
import org.kernelab.basis.gui.chart.TreeMapCanvasChart;
import org.kernelab.numeric.matrix.DoubleMatrix;
import org.kernelab.numeric.matrix.Size;
import org.kernelab.numeric.util.GaussJordanForm;

public class Numeric
{

	/**
	 * @param args
	 */
	public static void main(String[] args)
	{
		JFrame frame = new JFrame();

		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		TreeMapCanvasChart chart = new TreeMapCanvasChart();

		chart.setPreferredSize(new Dimension(400, 300));

		frame.add(chart);

		frame.pack();

		frame.setVisible(true);

		TreeMap<Double, Double> data = new TreeMap<Double, Double>();

		data.put(0.1, 1.377);
		data.put(0.2, 1.480);
		data.put(0.3, 1.693);
		data.put(0.4, 1.909);
		data.put(0.5, 2.255);
		data.put(0.6, 2.393);
		data.put(0.7, 2.771);
		data.put(0.8, 3.415);

		Function f = Numeric.PolynomialFitting(data, 3);

		chart.getData().put(data, new GraphicsPainterAdapter(Color.RED) {
			@Override
			public void paintLine(Graphics g, int x1, int y1, int x2, int y2)
			{

			}

			@Override
			public void paintNode(Graphics g, int x, int y)
			{
				if (color != null) {
					g.setColor(color);
				}
				g.drawLine(x - 2, y, x + 2, y);
				g.drawLine(x, y - 2, x, y + 2);
			}
		});
		chart.getFunction().put(f, new GraphicsPainterAdapter(Color.BLUE) {
			@Override
			public void paintLine(Graphics g, int x1, int y1, int x2, int y2)
			{
				if (color != null) {
					g.setColor(color);
				}
				g.drawLine(x1, y1, x2, y2);
			}
		});

		chart.viewData();
	}

	public static Function PolynomialFitting(Map<Double, Double> data, int exponent)
	{
		int rows = data.size();

		int columns = exponent + 1;

		DoubleMatrix coeff = new DoubleMatrix(new Size(rows, columns));

		DoubleMatrix value = new DoubleMatrix(new Size(rows, 1));

		List<Double> dataList = new ArrayList<Double>(data.size());

		Tools.listOfCollection(dataList, data.keySet());

		for (int row = 0; row < rows; row++) {

			double c = 1;
			double x = dataList.get(row);

			value.set(data.get(x), row, 0);

			for (int column = 0; column < columns; column++) {
				coeff.set(c, row, column);
				c *= x;
			}
		}

		DoubleMatrix coeft = coeff.cloneTranspose();

		coeff.product(coeft, coeff);

		value.product(coeft, value);

		coeff = new DoubleMatrix(coeff.joinRows(value));

		GaussJordanForm gjf = new GaussJordanForm();

		DoubleMatrix result = gjf.operate(coeff);

		final List<Double> polynomial = result.getListOfColumn(result.getColumns() - 1);

		Function function = new Function() {

			/**
			 * 
			 */
			private static final long	serialVersionUID	= 8519460784032303825L;

			@Override
			public double valueAt(double x)
			{
				double value = 0.0;

				double coeff = 1.0;

				for (Double p : polynomial) {
					value += coeff * p;
					coeff *= x;
				}

				return value;
			}

		};

		function.getDomain().add(
				new Interval<Double>(Tools.minOfCollection(dataList), Tools
						.maxOfCollection(dataList)));

		return function;
	}
}
